package com.xxmw.transaction.service.app.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xxmw.transaction.common.entity.P;
import com.xxmw.transaction.common.utils.Assert;
import com.xxmw.transaction.common.utils.Constant;
import com.xxmw.transaction.common.utils.SpringContextUtil;
import com.xxmw.transaction.common.utils.bourse.*;
import com.xxmw.transaction.enumeration.BourseEnum;
import com.xxmw.transaction.mapper.subscribe.SymbolSubMapper;
import com.xxmw.transaction.model.app.BourseApi;
import com.xxmw.transaction.model.app.BoursePrice;
import com.xxmw.transaction.model.app.Hedging;
import com.xxmw.transaction.model.subscribe.SymbolSub;
import com.xxmw.transaction.service.app.BourseApiService;
import com.xxmw.transaction.service.app.BrickOrderService;
import com.xxmw.transaction.service.app.SymbolSubService;
import com.xxmw.transaction.service.app.component.*;
import com.xxmw.transaction.service.app.event.SymbolAggregationPublisher;
import com.xxmw.transaction.service.support.Observable;
import com.xxmw.transaction.service.support.SubscribeWebSocket;
import com.xxmw.transaction.service.support.SymbolSubWebSocket;
import com.xxmw.transaction.vo.BrickableSymbol;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * @author luowenliang
 * @since 2020/2/18 14:01
 */
@Slf4j
@Service
public class SymbolSubServiceImpl extends ServiceImpl<SymbolSubMapper, SymbolSub> implements SymbolSubService {

	private List<Observable> list = new ArrayList<>();

    @Autowired
    private BourseApiService bourseApiService;

    @Autowired
    private SymbolAggregationPublisher symbolAggregationPublisher;



    @Override
    public P<SymbolSub> pageList(Integer pageNum, Integer pageSize, Integer bourse) {
        QueryWrapper<SymbolSub> queryWrapper = new QueryWrapper<>();
        queryWrapper.orderByDesc("created");
        queryWrapper.eq(null != bourse, "bourse", bourse);
        IPage<SymbolSub> page = new Page<>(pageNum, pageSize);
        this.page(page, queryWrapper);
        return new P<>(page.getTotal(), page.getRecords());
    }

    @Override
    public List<SymbolSub> openBrickList(String endWith) {
        QueryWrapper<SymbolSub> queryWrapper = new QueryWrapper<>();
        queryWrapper.likeLeft(StringUtils.isNotBlank(endWith), "symbol", endWith);
        queryWrapper.eq("status", 2);
        return list(queryWrapper);
    }

    @Override
    public List<SymbolSub> findBySymbolId(Integer symbolId) {
        QueryWrapper<SymbolSub> queryWrapper = new QueryWrapper<>();
        queryWrapper.likeLeft("symbol_id", symbolId);
        return list(queryWrapper);
    }

    @Override
    public SymbolSub getByBourseAndSymbol(Integer bourse, String symbol) {
        QueryWrapper<SymbolSub> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("bourse", bourse);
        queryWrapper.eq("symbol", symbol);
        return getOne(queryWrapper);
    }

    @Override
    public Set<String> listByBourse(Integer bourse) {
        return baseMapper.listByBourse(bourse);
    }

    @Override
    public List<SymbolSub> getBySymbol(String symbol) {
        QueryWrapper<SymbolSub> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("symbol", symbol);
        queryWrapper.eq("status", 2);
        return list(queryWrapper);
    }

    @Override
    public BoursePrice getPrice(Integer bourse, String symbol) {
    	SymbolSubWebSocket webSocket = WebSocketFactory.get(bourse);
        if (webSocket == null) {
            // websocket 未启动不返回错误，防止单个交易所错误导致系统一直搬砖失败
            log.error("交易所[****{}****]websocket未启动，获取价格失败", BourseEnum.getDescByValue(bourse));
            return null;
            // throw new TransactionException("交易所【***" + BourseEnum.getDescByValue(bourse)+ "***】Websocket未启动");
        }
        BoursePrice price = webSocket.getSymbolPrice(symbol);
        //JSONObject data = (JSONObject) symbolPrice.get("data");
       /* Date updateTime = data.getDate("updateTime");
        if (System.currentTimeMillis() - updateTime.getTime() > 6000) {
            // 数据无效
            throw new TransactionAdminException("订阅币种长时间未更新，请检查系统是否出现异常");
        }*/
        return price;
    }

    @Override
    public SubscribeWebSocket getWebSocket(Integer bourse) {
        return WebSocketFactory.get(bourse);
    }

    public Set<Integer> listBySymbol(String symbol) {
        Set<Integer> set = baseMapper.listBySymbol(symbol);
        return set;
    }

    /**
     * @param symbol 交易对
     * @return
     */
    @Override
    public void getHedging(String symbol) {
        //获取订阅了该币种的对应交易所
        Set<Integer> set = listBySymbol(symbol);

        if (CollectionUtils.isEmpty(set) || set.size() < 2) {
            return;
        }

        Integer[] array =new Integer[set.size()];
        Integer[] bourses = set.toArray(array);

        for (int i = 0; i < bourses.length - 1; i ++) {
            BoursePrice bourseOnePrice = getPrice(array[i], symbol);
            if (Assert.isNullOrEmpty(bourseOnePrice)) {
                continue;
            }
            for (int j = i + 1; j < bourses.length; j ++) {
                BoursePrice bourseTwoPrice = getPrice(array[j], symbol);
                if (Assert.isNullOrEmpty(bourseTwoPrice)) {
                    continue;
                }
                // log.info("交易所：{},买一价：{}，卖一价：{}", array[i], bourseOnePrice.getBuyPrice(), bourseOnePrice.getSellPrice());
                // log.info("交易所：{},买一价：{}，卖一价：{}", array[j], bourseTwoPrice.getBuyPrice(), bourseTwoPrice.getSellPrice());
                // 一号交易所的买一价比二号交易所的卖一价高
                if (Assert.isNotNullOrEmpty(bourseOnePrice.getBuyPrice())
                        && Assert.isNotNullOrEmpty(bourseTwoPrice.getSellPrice())
                        && bourseOnePrice.getBuyPrice().compareTo(bourseTwoPrice.getSellPrice()) > 0) {
                    Hedging hedging = new Hedging();
                    hedging.setSymbol(symbol);

                    // 一号交易所为最高买单
                    hedging.setBuyPrice(bourseOnePrice.getBuyPrice());
                    hedging.setBuyData(bourseOnePrice.getData());
                    hedging.setBuyBourse(array[i]);

                    // 二号交易所为最低卖单
                    hedging.setSellBourse(array[j]);
                    hedging.setSellPrice(bourseTwoPrice.getSellPrice());
                    hedging.setSellData(bourseTwoPrice.getData());
                    verifyProfit(hedging);

                } else if (Assert.isNotNullOrEmpty(bourseTwoPrice.getBuyPrice())
                        && Assert.isNotNullOrEmpty(bourseOnePrice.getSellPrice())
                        && bourseTwoPrice.getBuyPrice().compareTo(bourseOnePrice.getSellPrice()) > 0) {
                    // 二号交易所的买一价比一号交易所的卖一价高
                    Hedging hedging = new Hedging();
                    hedging.setSymbol(symbol);

                    // 二号交易所为最高买单
                    hedging.setBuyPrice(bourseTwoPrice.getBuyPrice());
                    hedging.setBuyData(bourseTwoPrice.getData());
                    hedging.setBuyBourse(array[j]);

                    // 一号交易所为最低卖单
                    hedging.setSellBourse(array[i]);
                    hedging.setSellPrice(bourseOnePrice.getSellPrice());
                    hedging.setSellData(bourseOnePrice.getData());
                    verifyProfit(hedging);
                }
            }
        }
        // 清除数据  防止webSocket接口没有更新数据重复使用旧数据
        cleanData(symbol, set);
    }

    @Override
    public List<BrickableSymbol> getBrickableSymbol() {
        return baseMapper.getBrickableSymbol();
    }

    @Override
    public BigDecimal getUSDTAmount(BourseApi api) {
        BigDecimal amount = null;
        switch (api.getBourse()) {
            case 1:
                //币查询余额
                amount = HuobiUtil.getBalance(api, "usdt").getAvailable();
                break;
            case 2:
                //币安查询
                amount = BinanceUtil.getBalance(api, "usdt").getAvailable();
                break;
            case 3:
                amount = OkexUtil.getBalance(api, "usdt").getAvailable();
                break;
            case 4:
                amount = BitforexUtil.getBalance(api, "usdt").getAvailable();
                break;
            case 5:
                amount = ZBUtil.getBalance(api, "usdt").getAvailable();
                break;
            case 6:
                amount = PoloniexUtil.getBalance(api, "usdt").getAvailable();
                break;
            case 7:
                amount = JiuBiUtil.getBalance(api, "usdt").getAvailable();
                break;
            case 8:
                amount = BikiUtil.getBalance(api, "usdt").getAvailable();
                break;
            default:
                ;
        }
        return amount;
    }

    @Override
    public BigDecimal getUSDTAmount(Long apiId) {
        BigDecimal amount = null;
        BourseApi api = bourseApiService.getById(apiId);
        switch (api.getBourse()) {
            case 1:
                //币查询余额
                amount = HuobiUtil.getBalance(api, "usdt").getAvailable();
                break;
            case 2:
                //币安查询
                amount = BinanceUtil.getBalance(api, "usdt").getAvailable();
                break;
            case 3:
                amount = OkexUtil.getBalance(api, "usdt").getAvailable();
                break;
            case 4:
                amount = BitforexUtil.getBalance(api, "usdt").getAvailable();
                break;
            case 5:
                amount = ZBUtil.getBalance(api, "usdt").getAvailable();
                break;
            case 6:
                amount = PoloniexUtil.getBalance(api, "usdt").getAvailable();
                break;
            case 7:
                amount = JiuBiUtil.getBalance(api, "usdt").getAvailable();
                break;
            case 8:
                amount = BikiUtil.getBalance(api, "usdt").getAvailable();
                break;
            default:
                ;
        }
        return amount;
    }

    /**
     * 清除交易完成后数据
     * @param symbol
     * @param bourseSet 需要清除数据的交易所
     */
    private static void cleanData (String symbol, Set<Integer> bourseSet) {
    	bourseSet.forEach(b -> {
    		SymbolSubWebSocket webSocket = WebSocketFactory.get(b);
    		if(webSocket != null) webSocket.cleanSymbolPrice(symbol);
    	});
    }

    private void verifyProfit (Hedging hedging) {
        //总价格 = 买卖价格之和
        BigDecimal totalPrice = hedging.getBuyPrice().add(hedging.getSellPrice());

        //手续费 = 总价格 * 系统手续费
        BigDecimal fee = totalPrice.multiply(new BigDecimal(Constant.BRICK_FEE));
        //净利润 = 买卖差值  - 手续费
        BigDecimal netProfit = hedging.getSellPrice().subtract(hedging.getBuyPrice()).abs().subtract(fee);
        //利润率
        BigDecimal profitMargin = netProfit.divide(totalPrice, 4, BigDecimal.ROUND_HALF_UP);

        hedging.setProfitMargin(profitMargin);

//        log.info("买一价：{}，卖一价：{}, 买一交易所：{}， 卖一交易所：{}",hedging.getBuyPrice(), hedging.getSellPrice(), hedging.getBuyBourse(), hedging.getSellBourse());
//        log.info("当前利润率：{}", profitMargin);
        // 收益率大于0
        if (profitMargin.compareTo(BigDecimal.ZERO) > 0) {
            symbolAggregationPublisher.publish(hedging);
        }
    }

    private static class WebSocketFactory {

        public static SymbolSubWebSocket get(int type) {
        	SymbolSubWebSocket symbolSubWebSocket;
            switch (type) {
                case 1:
                	symbolSubWebSocket = SpringContextUtil.getBean(HuoBiTickerApplication.class);
                    break;
                case 2:
                	symbolSubWebSocket = SpringContextUtil.getBean(BiAnTickerApplication.class);
                    break;
                case 3:
                	symbolSubWebSocket = SpringContextUtil.getBean(OkexTickerApplication.class);
                    break;
                case 4:
                	symbolSubWebSocket = SpringContextUtil.getBean(BitforexApplication.class);
                    break;
                case 5:
                    symbolSubWebSocket = SpringContextUtil.getBean(ZBApplication.class);
                    break;
                case 6:
                    symbolSubWebSocket = SpringContextUtil.getBean(PoloniexApplication.class);
                    break;
                case 8:
                    symbolSubWebSocket = SpringContextUtil.getBean(BiKiApplication.class);
                    break;
                case 7:
                    symbolSubWebSocket = SpringContextUtil.getBean(JiuBiApplication.class);
                    break;
                default:
                	symbolSubWebSocket = null;
            }
            return symbolSubWebSocket;
        }
    }


	@Override
	public void attach(Observable observable) {
		list.add(observable);
	}

	@Override
	public void changed(Hedging message) {
		list.forEach(o -> o.watch(message));
	}

}
